再战朋友圈
本文是《技术相关(共39篇)》目录的第 39 篇。阅读本文前,建议先阅读本文前3篇文章:
朋友圈从开始前前后后经历了几次改造,前天晚上决定还是换一种方式。原因如下:部分友链网站更新并不及时,个别已经一年未更新,并且朋友圈前台速度我也一直不满意,趁这次修改,把解析友链网站RSS的逻辑也变更了一下。
新逻辑如下:解析单个友链网站前5篇文章,一旦获取成功,该站解析进程立刻终止。并且这5篇文章只保留网站名称、站标、文章标题、文章链接及发布(更新)日期,其它项目及5篇文章以后的全部删除,依然保存在rsscache目录下。待所有网站获取成功后,按照发布(更新)日期进行排序,一旦数量达到30篇,所有流程结束,后面的文章全部忽略,以卡片的形式只显示前30篇。
这样的好处是,以前都是解析所有友链网站的整个rss文件,少则几十KB,多则二百多KB,现在都在5KB以内。尽管前台显示速度还是没有达到我要的效果,不过比之前算是有了质的飞跃。
改造后的代码如下:
一、如果你的PHP安装的有redis扩展,使用如下代码。(非常推荐)
[reply]
<?php
/*
Template Name: 似水流年-RSS 朋友圈
*/
// 设置时区
date_default_timezone_set('America/New_York');
// 获取页面头部信息
get_header();
// 引入 SimplePie 类
require_once(ABSPATH. WPINC. '/class-simplepie.php');
// 输出页面主内容区域的 HTML 部分
echo '<div id="primary" class="content-area">';
echo '<main id="main" class="site-main" role="main">';
echo '<p style="color: red; font-size: 14px; margin-top: 10px; margin-bottom: 20px; text-align: center; display: flex; align-items: center; justify-content: center;">以下友情链接网站最新内容每 2 小时获取更新一次</p>';
// 定义缓存更新时间间隔
$cache_update_interval = 2 * 3600;
// 验证 URL 合法性
function is_valid_url($url) {
return filter_var($url, FILTER_VALIDATE_URL)!== false;
}
// 连接 Redis 服务器
function connect_redis() {
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
return $redis;
}
// 检查 Redis 中是否存在缓存且未过期
function is_redis_cache_valid($redis, $key, $cache_update_interval) {
$last_update = $redis->get($key. ':last_update');
if ($last_update) {
return (time() - $last_update < $cache_update_interval);
}
return false;
}
// 存储 RSS 数据到 Redis
function store_rss_in_redis($redis, $key, $rss_content) {
$redis->set($key, $rss_content);
$redis->set($key. ':last_update', time());
}
// 处理单个链接的 RSS 数据
function process_single_link_rss_data($link, $cache_update_interval) {
// 检查 URL 合法性和解析域名
if (!is_valid_url($link->link_url) ||!is_valid_url($link->link_rss)) {
return [];
}
$domain = parse_url($link->link_url, PHP_URL_HOST);
if (!$domain) {
return [];
}
$redis = connect_redis();
$key ='rsscache:'. $domain;
$site_articles = [];
$count_site_articles = 0;
// 检查 Redis 缓存是否需要更新
if (is_redis_cache_valid($redis, $key, $cache_update_interval)) {
$rss_content = $redis->get($key);
$rss = new SimplePie();
$rss->set_raw_data($rss_content);
$rss->init();
$rss_items = $rss->get_items();
foreach ($rss_items as $item) {
if ($count_site_articles >= 5) {
break;
}
$item_data = array(
'link_name' => $link->link_name,
'link_logo' => $link->link_image,
'link_url' => $link->link_url,
'item_title' => $item->get_title(),
'item_link' => $item->get_permalink(),
'item_date' => $item->get_date('Y-m-d H:i:s'),
'formatted_date' => date('Y-m-d', strtotime($item->get_date('Y-m-d H:i:s'))),
'item_author' => $item->get_author()->get_name(),
'item_excerpt' => $item->get_description(),
'item_content' => strip_tags($item->get_content(), '<p><br>'),
);
$site_articles[] = $item_data;
$count_site_articles++;
}
return $site_articles;
}
// 获取并处理 RSS 内容
$rss_content = get_rss_content($link->link_rss);
if ($rss_content && $rss_content!= '无法从提供 RSS 的 URL 获取 RSS 内容。') {
$processed_rss_content = process_rss_content($rss_content);
store_rss_in_redis($redis, $key, $processed_rss_content);
$rss = new SimplePie();
$rss->set_raw_data($processed_rss_content);
$rss->init();
$rss_items = $rss->get_items();
foreach ($rss_items as $item) {
if ($count_site_articles >= 5) {
break;
}
$item_data = array(
'link_name' => $link->link_name,
'link_logo' => $link->link_image,
'link_url' => $link->link_url,
'item_title' => $item->get_title(),
'item_link' => $item->get_permalink(),
'item_date' => $item->get_date('Y-m-d H:i:s'),
'formatted_date' => date('Y-m-d', strtotime($item->get_date('Y-m-d H:i:s'))),
'item_author' => $item->get_author()->get_name(),
'item_excerpt' => $item->get_description(),
'item_content' => strip_tags($item->get_content(), '<p><br>'),
);
$site_articles[] = $item_data;
$count_site_articles++;
}
}
return $site_articles;
}
// 获取并缓存友情链接的 RSS 数据
function get_cached_friend_link_rss_data($cache_update_interval) {
$all_articles = [];
$links = get_bookmarks(array(
'orderby' => 'name',
'category' => 0,
'order' => 'ASC',
'hide_invisible' => 1,
'categorize' => 0,
'title_li' => '',
'echo' => 0
));
foreach ($links as $link) {
if ($link->link_rss &&!empty($link->link_rss)) {
$site_articles = process_single_link_rss_data($link, $cache_update_interval);
$all_articles = array_merge($all_articles, $site_articles);
}
}
return $all_articles;
}
// 获取 RSS 内容
function get_rss_content($feed_url) {
$response = wp_safe_remote_get($feed_url, array('sslverify' => true));
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
} else {
$response = wp_safe_remote_get($feed_url, array('sslverify' => false));
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
} else {
error_log("无法从提供 RSS 的 URL: {$feed_url} 获取 RSS 内容,错误信息: ". print_r( $response, true));
return '无法从提供 RSS 的 URL 获取 RSS 内容。';
}
}
}
// 处理 RSS 内容
function process_rss_content($rss_content) {
$dom = new DOMDocument();
// 清除可能存在的 BOM 头
$rss_content = preg_replace('/^\xEF\xBB\xBF/', '', $rss_content);
// 去除多余的空格和换行符
$rss_content = trim($rss_content);
// 尝试加载 XML 内容
if ($dom->loadXML($rss_content)) {
$items = $dom->getElementsByTagName('item');
$channel = $dom->getElementsByTagName('channel')->item(0);
// 处理前 5 个 <item> 元素
for ($i = 0; $i < min(5, $items->length); $i++) {
$item = $items->item($i);
$categoryNodes = $item->getElementsByTagName('category');
foreach ($categoryNodes as $categoryNode) {
while ($categoryNode->hasChildNodes()) {
$categoryNode->removeChild($categoryNode->firstChild);
}
}
$descriptionNodes = $item->getElementsByTagName('description');
foreach ($descriptionNodes as $descriptionNode) {
while ($descriptionNode->hasChildNodes()) {
$descriptionNode->removeChild($descriptionNode->firstChild);
}
}
$contentEncodedNodes = $item->getElementsByTagNameNS('http://purl.org/rss/1.0/modules/content/', 'encoded');
foreach ($contentEncodedNodes as $contentEncodedNode) {
while ($contentEncodedNode->hasChildNodes()) {
$contentEncodedNode->removeChild($contentEncodedNode->firstChild);
}
}
}
// 删除第 5 个 <item> 元素后的内容
$countItemEndTags = 0;
$childNodes = $channel->childNodes;
$removeStartIndex = -1;
for ($i = 0; $i < $childNodes->length; $i++) {
$node = $childNodes->item($i);
if ($node->nodeName == 'item') {
$countItemEndTags++;
if ($countItemEndTags == 5) {
$removeStartIndex = $i + 1;
}
}
}
if ($removeStartIndex!= -1) {
$fragment = $dom->createDocumentFragment();
for ($i = 0; $i < $removeStartIndex; $i++) {
$fragment->appendChild($childNodes->item($i)->cloneNode(true));
}
for ($i = $removeStartIndex; $i < $childNodes->length; $i++) {
$node = $childNodes->item($i);
if ($node->nodeName == 'channel') {
$fragment->appendChild($node->cloneNode(true));
break;
}
}
while ($channel->hasChildNodes()) {
$channel->removeChild($channel->firstChild);
}
$channel->appendChild($fragment);
}
return $dom->saveXML();
} else {
// 处理加载 XML 失败的情况
error_log("Failed to load XML content from RSS feed.");
return '';
}
}
// 获取最新的 30 篇友情链接文章
function get_latest_friend_link_articles($cache_update_interval) {
$latest_articles = get_cached_friend_link_rss_data($cache_update_interval);
usort($latest_articles, function($a, $b) {
$dateA = new DateTime($a['item_date']);
$dateB = new DateTime($b['item_date']);
return $dateB <=> $dateA;
});
$sorted_articles = array_slice($latest_articles, 0, 30);
return $sorted_articles;
}
// 获取并显示友情链接的最新文章
$cache_update_interval = 2 * 3600; // 2 小时
$latest_articles = get_latest_friend_link_articles($cache_update_interval);
?>
<div class="row">
<?php foreach ($latest_articles as $article) {
?>
<div class="col-sm-4">
<div class="card" style="background-color: white; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); margin-bottom: 15px;">
<div class="card-body" style="display: flex; flex-direction: column; height: 100%;">
<div style="flex: 4; display: flex;">
<div style="flex: 4; padding: 5px; overflow: hidden; display: flex; align-items: center;">
<a href="<?php echo esc_url($article['item_link']);?>" target="_blank" style="font-size: 14px;"><?php echo esc_html($article['item_title']);?></a>
</div>
<div style="flex: 1; padding: 5px; display: flex; justify-content: center; align-items: center;">
<img src="<?php echo esc_url($article['link_logo']);?>" alt="<?php echo esc_attr($article['link_name']);?> Logo" style="width: 100%; height: 100%; object-fit: contain;">
</div>
</div>
<div style="flex: 1; display: flex;">
<div style="flex: 1; padding: 5px; display: flex; align-items: center;">
<a href="<?php echo esc_url($article['link_url']);?>" target="_blank" style="font-size: 14px;"><?php echo esc_html($article['link_name']);?></a>
</div>
<div style="flex: 1; padding: 5px; display: flex; justify-content: flex-end; align-items: center;">
<span style="font-size: 14px;"><?php echo esc_html($article['formatted_date']);?></span>
</div>
</div>
</div>
</div>
</div>
<?php }?>
</div>
<?php
// 获取并显示侧边栏
get_sidebar();
echo '</main><!-- #main -->';
echo '</div><!-- #primary -->';
// 获取并显示页面底部信息
get_footer();
?>
[/reply]
二、如果你的PHP没有安装redis扩展,使用以下代码。
[reply]
<?php
/*
Template Name: 似水流年-RSS 朋友圈
*/
// 设置时区
date_default_timezone_set('America/New_York');
// 获取页面头部信息
get_header();
// 引入 SimplePie 类
require_once(ABSPATH. WPINC. '/class-simplepie.php');
// 输出页面主内容区域的 HTML 部分
echo '<div id="primary" class="content-area">';
echo '<main id="main" class="site-main" role="main">';
echo '<p style="color: red; font-size: 14px; margin-top: 10px; margin-bottom: 20px; text-align: center; display: flex; align-items: center; justify-content: center;">以下友情链接网站最新内容每 2 小时获取更新一次</p>';
// 定义缓存更新时间间隔
$cache_update_interval = 2 * 3600;
// 验证 URL 合法性
function is_valid_url($url) {
return filter_var($url, FILTER_VALIDATE_URL)!== false;
}
// 处理单个链接的 RSS 数据
function process_single_link_rss_data($link, $cache_update_interval) {
// 检查 URL 合法性和解析域名
if (!is_valid_url($link->link_url) ||!is_valid_url($link->link_rss)) {
return [];
}
$domain = parse_url($link->link_url, PHP_URL_HOST);
if (!$domain) {
return [];
}
$cache_file = get_stylesheet_directory(). '/rsscache/'. $domain. '.xml';
$site_articles = [];
$count_site_articles = 0;
// 检查缓存是否需要更新
if (file_exists($cache_file)) {
$rss_items = get_rss_items($cache_file);
$latest_item_date = null;
foreach ($rss_items as $item) {
$date = new DateTime($item->get_date('Y-m-d H:i:s'));
$date->setTimezone(new DateTimeZone('America/New_York'));
$item_date = $date->getTimestamp();
if ($latest_item_date === null || $item_date > $latest_item_date) {
$latest_item_date = $item_date;
}
}
if ($latest_item_date!== null && (time() - $latest_item_date < $cache_update_interval) ) {
// 从缓存中获取文章信息
foreach ($rss_items as $item) {
if ($count_site_articles >= 5) {
break;
}
$item_data = array(
'link_name' => $link->link_name,
'link_logo' => $link->link_image,
'link_url' => $link->link_url,
'item_title' => $item->get_title(),
'item_link' => $item->get_permalink(),
'item_date' => $item->get_date('Y-m-d H:i:s'),
'formatted_date' => date('Y-m-d', strtotime($item->get_date('Y-m-d H:i:s'))),
'item_author' => $item->get_author()->get_name(),
'item_excerpt' => $item->get_description(),
'item_content' => strip_tags($item->get_content(), '<p><br>'),
);
$site_articles[] = $item_data;
$count_site_articles++;
}
return $site_articles;
}
}
// 获取并处理 RSS 内容
$rss_content = get_rss_content($link->link_rss);
if ($rss_content && $rss_content!= '无法从提供 RSS 的 URL 获取 RSS 内容。') {
$processed_rss_content = process_rss_content($rss_content);
// 确保目录存在
if (!is_dir(dirname($cache_file))) {
mkdir(dirname($cache_file), 0755, true);
}
file_put_contents($cache_file, $processed_rss_content);
$rss_items = get_rss_items($cache_file);
foreach ($rss_items as $item) {
if ($count_site_articles >= 5) {
break;
}
$item_data = array(
'link_name' => $link->link_name,
'link_logo' => $link->link_image,
'link_url' => $link->link_url,
'item_title' => $item->get_title(),
'item_link' => $item->get_permalink(),
'item_date' => $item->get_date('Y-m-d H:i:s'),
'formatted_date' => date('Y-m-d', strtotime($item->get_date('Y-m-d H:i:s'))),
'item_author' => $item->get_author()->get_name(),
'item_excerpt' => $item->get_description(),
'item_content' => strip_tags($item->get_content(), '<p><br>'),
);
$site_articles[] = $item_data;
$count_site_articles++;
}
}
return $site_articles;
}
// 获取并缓存友情链接的 RSS 数据
function get_cached_friend_link_rss_data($cache_update_interval) {
$all_articles = [];
$links = get_bookmarks(array(
'orderby' => 'name',
'category' => 0,
'order' => 'ASC',
'hide_invisible' => 1,
'categorize' => 0,
'title_li' => '',
'echo' => 0
));
foreach ($links as $link) {
if ($link->link_rss &&!empty($link->link_rss)) {
$site_articles = process_single_link_rss_data($link, $cache_update_interval);
$all_articles = array_merge($all_articles, $site_articles);
}
}
return $all_articles;
}
// 获取 RSS 内容
function get_rss_content($feed_url) {
$response = wp_safe_remote_get($feed_url, array('sslverify' => true));
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
} else {
$response = wp_safe_remote_get($feed_url, array('sslverify' => false));
if (!is_wp_error($response) && wp_remote_retrieve_response_code($response) === 200) {
return wp_remote_retrieve_body($response);
} else {
error_log("无法从提供 RSS 的 URL: {$feed_url} 获取 RSS 内容,错误信息: ". print_r( $response, true));
return '无法从提供 RSS 的 URL 获取 RSS 内容。';
}
}
}
// 处理 RSS 内容
function process_rss_content($rss_content) {
$dom = new DOMDocument();
// 清除可能存在的 BOM 头
$rss_content = preg_replace('/^\xEF\xBB\xBF/', '', $rss_content);
// 去除多余的空格和换行符
$rss_content = trim($rss_content);
// 尝试加载 XML 内容
if ($dom->loadXML($rss_content)) {
$items = $dom->getElementsByTagName('item');
$channel = $dom->getElementsByTagName('channel')->item(0);
// 处理前 5 个 <item> 元素
for ($i = 0; $i < min(5, $items->length); $i++) {
$item = $items->item($i);
$categoryNodes = $item->getElementsByTagName('category');
foreach ($categoryNodes as $categoryNode) {
while ($categoryNode->hasChildNodes()) {
$categoryNode->removeChild($categoryNode->firstChild);
}
}
$descriptionNodes = $item->getElementsByTagName('description');
foreach ($descriptionNodes as $descriptionNode) {
while ($descriptionNode->hasChildNodes()) {
$descriptionNode->removeChild($descriptionNode->firstChild);
}
}
$contentEncodedNodes = $item->getElementsByTagNameNS('http://purl.org/rss/1.0/modules/content/', 'encoded');
foreach ($contentEncodedNodes as $contentEncodedNode) {
while ($contentEncodedNode->hasChildNodes()) {
$contentEncodedNode->removeChild($contentEncodedNode->firstChild);
}
}
}
// 删除第 5 个 <item> 元素后的内容
$countItemEndTags = 0;
$childNodes = $channel->childNodes;
$removeStartIndex = -1;
for ($i = 0; $i < $childNodes->length; $i++) {
$node = $childNodes->item($i);
if ($node->nodeName == 'item') {
$countItemEndTags++;
if ($countItemEndTags == 5) {
$removeStartIndex = $i + 1;
}
}
}
if ($removeStartIndex!= -1) {
$fragment = $dom->createDocumentFragment();
for ($i = 0; $i < $removeStartIndex; $i++) {
$fragment->appendChild($childNodes->item($i)->cloneNode(true));
}
for ($i = $removeStartIndex; $i < $childNodes->length; $i++) {
$node = $childNodes->item($i);
if ($node->nodeName == 'channel') {
$fragment->appendChild($node->cloneNode(true));
break;
}
}
while ($channel->hasChildNodes()) {
$channel->removeChild($channel->firstChild);
}
$channel->appendChild($fragment);
}
return $dom->saveXML();
} else {
// 处理加载 XML 失败的情况
error_log("Failed to load XML content from RSS feed.");
return '';
}
}
// 使用 SimplePie 获取 RSS 项
function get_rss_items($cache_file) {
$rss = new SimplePie();
$rss->set_feed_url($cache_file);
$rss->init();
$rss->handle_content_type();
$rss->set_output_encoding('UTF-8');
return $rss->get_items();
}
// 获取最新的 30 篇友情链接文章
function get_latest_friend_link_articles($cache_update_interval) {
$latest_articles = get_cached_friend_link_rss_data($cache_update_interval);
usort($latest_articles, function($a, $b) {
$dateA = new DateTime($a['item_date']);
$dateB = new DateTime($b['item_date']);
return $dateB <=> $dateA;
});
$sorted_articles = array_slice($latest_articles, 0, 30);
return $sorted_articles;
}
// 获取并显示友情链接的最新文章
$latest_articles = get_latest_friend_link_articles($cache_update_interval);
?>
<div class="row">
<?php foreach ($latest_articles as $article) {
?>
<div class="col-sm-4">
<div class="card" style="background-color: white; border-radius: 10px; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); margin-bottom: 15px;">
<div class="card-body" style="display: flex; flex-direction: column; height: 100%;">
<div style="flex: 4; display: flex;">
<div style="flex: 4; padding: 5px; overflow: hidden; display: flex; align-items: center;">
<a href="<?php echo esc_url($article['item_link']);?>" target="_blank" style="font-size: 14px;"><?php echo esc_html($article['item_title']);?></a>
</div>
<div style="flex: 1; padding: 5px; display: flex; justify-content: center; align-items: center;">
<img src="<?php echo esc_url($article['link_logo']);?>" alt="<?php echo esc_attr($article['link_name']);?> Logo" style="width: 100%; height: 100%; object-fit: contain;">
</div>
</div>
<div style="flex: 1; display: flex;">
<div style="flex: 1; padding: 5px; display: flex; align-items: center;">
<a href="<?php echo esc_url($article['link_url']);?>" target="_blank" style="font-size: 14px;"><?php echo esc_html($article['link_name']);?></a>
</div>
<div style="flex: 1; padding: 5px; display: flex; justify-content: flex-end; align-items: center;">
<span style="font-size: 14px;"><?php echo esc_html($article['formatted_date']);?></span>
</div>
</div>
</div>
</div>
</div>
<?php }?>
</div>
<?php
// 获取并显示侧边栏
get_sidebar();
echo '</main><!-- #main -->';
echo '</div><!-- #primary -->';
// 获取并显示页面底部信息
get_footer();
?>
[/reply]
扶苏
我来瞅一瞅最新版
沉沦
你的站就不提供全文摘要,所以我就通过 p 标签来提取全文,然而你边栏也有 p 标签包裹的文字:Luohe,Henan打工仔Worker 所以你的文章每篇前面都带有这个😂
似水流年
确实要缩小P标签的范围。
沉沦
现在这个效果不错啊,我一直在犹豫还要不要保留摘要...主要是有的博客并不提供全文摘要,有的文章可能就一句话就没了...造成摘要截断行数不足四行。如果通过 css 过滤器获取完文的话,获取的全文格式又五花八门还要再次过滤,真的是人都麻了~
似水流年
这就是我去掉摘要的原因。
刘郎
我看看你咋弄的 哈哈
似水流年
不停的折腾😸
acevs
mark有个印象。
似水流年
欢迎mark
obaby
你把每个站点单独存了啊?我就写到了一个文件里面。
似水流年
是的,单独存放了,没有放到一个文件里。本来想着json格式的,但是没成功。